package project;
import java.sql.*;
import java.io.*;
import java.security.*;
import java.lang.Math;
import java.util.ArrayList;


public class ChecksumSkew{
    DatabaseInfo db;
    private int checkSum = 0;
    private long checkHash = 0;
    public int getCheckSum(){
        return checkSum;
    }
    public long getCheckHash(){
        return checkHash;
    }

//    public ChecksumSkew(int skew) {
//
//    try {
//           Class.forName("com.mysql.jdbc.Driver").newInstance();
//	} catch (Exception e) {
//            System.out.println("Error loading Driver");
//	}
//    try {
//        FileInputStream fis = new FileInputStream("local.dat");
//        ObjectInputStream ois = new ObjectInputStream(fis);
//        db = (DatabaseInfo)ois.readObject();
//        int x = getRowNumbers(db);
//        try{
//        checkHash = calculateChecksumB(db, 1, 1, skew);
//        } catch (SingleRecordException e){
//
//        }
//       // System.out.println(checkSum+"local 1x");
//
//    } catch (IOException e){
//        System.err.println(e.getMessage());
//    } catch (ClassNotFoundException e){
//        System.err.println(e.getMessage());
//    }
//}

public ChecksumSkew(DatabaseInfo database, int fraction, int position, int skew, int part) throws SingleRecordException{
    try {
           Class.forName("com.mysql.jdbc.Driver").newInstance();
	} catch (Exception e) {
            System.err.println("Error loading Driver");
	}
    checkSum = calculateChecksumB(database, fraction, position, skew, part);

   // System.out.println(checkSum+ "other ab");
}

public ChecksumSkew(int fraction, int position, int skew, int part) throws SingleRecordException{
    try {
           Class.forName("com.mysql.jdbc.Driver").newInstance();
	} catch (Exception e) {
            System.err.println("Error loading Driver");
	}
    try {
        FileInputStream fis = new FileInputStream("local.dat");
        ObjectInputStream ois = new ObjectInputStream(fis);
        db = (DatabaseInfo)ois.readObject();
        checkSum = calculateChecksumB(db, fraction, position, skew, part);
        //System.out.println(checkSum + "local ab");

    } catch (IOException e){
        System.err.println(e.getMessage());
    } catch (ClassNotFoundException e){
        System.err.println(e.getMessage());
    }
}
public int getRowNumbers(DatabaseInfo db){
    int n = 0;
    Connection conn = null;
    try{
    conn = DriverManager.getConnection("jdbc:mysql://" + db.getHostName()+":"+db.getPortNo()+"/"+db.getDatabaseName(),
		db.getUserName(),db.getPassword());
        Statement stmt = conn.createStatement();
        ResultSet rs = conn.getMetaData().getTables("", "", "", null);
        while (rs.next()){
            ResultSet rows = stmt.executeQuery("Select count(*) from " + rs.getString(3));
            while (rows.next()){
                n+= Integer.parseInt(rows.getString(1));
            }
        }
        conn.close();
    } catch (SQLException e){
        System.err.println(e.getMessage());
    }
    return n;
}
public  int calculateChecksumB(DatabaseInfo db, int fraction, int position, int skew, int part) throws SingleRecordException{
    int i = 0;
    int numRows = getRowNumbers(db);
    double startScale = 0;
    double finishScale = 1;
    if (skew == 2){
    for (int j=1;j<fraction;j*=2){
        double a = startScale;double b = finishScale;
        double x = (fraction/j)/position;
        if (x >=2){
            b = (finishScale - startScale) * 0.75 + startScale;
        } else {
            a = (finishScale - startScale) * 0.75 + startScale;
        }
        startScale = a; finishScale = b;
    }
    } else if (skew == 1){
    for (int j=1;j<fraction;j*=2){
        double a = startScale;double b = finishScale;
        double x = (fraction/j)/position;
        if (x >=2){
            b = (finishScale - startScale) * 0.25 + startScale;
        } else {
            a = (finishScale - startScale) * 0.25 + startScale;
        }
        startScale = a; finishScale = b;
    }
    }
    double y = numRows * startScale;
    int start = (int)y +1;

    y = numRows * finishScale;
    int finish = (int)y;
System.out.println("    Start: "+ start + ", Finish: "+finish);
if (finish - start >= 0){
    int recordCount = start;
    Connection conn = null;
    ResultSet values = null;
    ResultSet tables = null;
    try{
        conn = DriverManager.getConnection("jdbc:mysql://" + db.getHostName()+":"+db.getPortNo()+"/"+db.getDatabaseName(),
		db.getUserName(),db.getPassword());
        Statement stmt = conn.createStatement();
        tables = conn.getMetaData().getTables("", "", "", null);
        int endPoint = finish;
    while (tables.next()) {
        ResultSet rows = stmt.executeQuery("select count(*) from "+ tables.getString(3));

        rows.absolute(1);
        int rowNum = Integer.parseInt(rows.getString(1));
        rows.close();
        if (rowNum < recordCount) {
            recordCount -= rowNum;
            endPoint -= rowNum;
        } else {
            values = stmt.executeQuery("select * from " + tables.getString(3));

            if (recordCount >1){
            values.absolute(recordCount-1);

            }
            while (values.next() && recordCount <= endPoint){

                int j = 1;
        while (true){
            try{
            //System.out.print(values.getString(j) + " ");
            byte[] byteArray = getBytes(values.getString(j));
            //i += adler32(byteArray) % 65521;
            i += fletcher(byteArray) % 65535;
            //i += sha1(byteArray);
            j++;

            } catch (Exception e){
                break;
            }
        }
                    //i += Integer.parseInt(values.getString(1));
                recordCount++;
            }

       recordCount -= rowNum;
        endPoint -= rowNum;
        }

    }

    conn.close();
    } catch (SQLException e) {
		System.err.println(e.getMessage());//
    }
if (start == finish){
    throw new SingleRecordException();
}
}
    return i;
}
  public byte[] getBytes(Object obj) throws IOException{
      ByteArrayOutputStream bos = new ByteArrayOutputStream();
      ObjectOutputStream oos = new ObjectOutputStream(bos);
      oos.writeObject(obj);
      oos.flush();
      oos.close();
      bos.close();
      byte [] data = bos.toByteArray();
      return data;
  }
  public int adler32(byte[] bytes){
     // System.out.println("adler32");
      int a = 1;
      int b = 0;
      for (int j=0;j<bytes.length;j++){
          a += bytes[j] % 65521;
          b += a % 65521;
      }
      return (b << 16 | a);
  }

public int fletcher(byte[] bytes){
      //System.out.println("Fletcher");
      int a = 1;
      int b = 0;
      for (int j=0;j<bytes.length;j+=2){
          try{
          a += (bytes[j] << 8 | bytes[j+1]) % 65535;
          } catch (IndexOutOfBoundsException e){
              a += (bytes[j]<<8) % 65535;
          }
          b += a % 65535;
      }
      return (b << 16 | a);
  }
}
